import {createStorage} from "./storage";
import {DEFAULT_CACHE_TIME, LOCAL_CACHE_KEY, SESSION_CACHE_KEY} from "./token";

const ls = createStorage({storage: localStorage});
const ss = createStorage({storage: sessionStorage});

interface Storage {
    value: any,
    expire: number
}

const isStorageValid = ({value, expire}: Storage) => {
    return value !== undefined && value !== null && (expire === -1 || expire > Date.now())
}

export const setupStorage = () => {
    const unloadHandler = () => {

        //+-----------------------------------------------------------
        // localStorage
        //+-----------------------------------------------------------
        const localCache: Record<string, Storage> = {};
        const localStorage = ls.get(LOCAL_CACHE_KEY);
        for (let key in localStorage) {
            const lsStorage = localStorage[key]
            if (isStorageValid(lsStorage)) {
                localCache[key] = lsStorage
            }
        }
        ls.set(LOCAL_CACHE_KEY, localCache)

        //+-----------------------------------------------------------
        // sessionStorage
        //+-----------------------------------------------------------
        const sessionCache: Record<string, Storage> = {}
        const sessionStorage = ls.get(SESSION_CACHE_KEY);
        for (let key in sessionStorage) {
            const ssStorage = sessionStorage[key];
            if (isStorageValid(ssStorage)) {
                sessionCache[key] = ssStorage
            }
        }
        ss.set(SESSION_CACHE_KEY, sessionCache)
    }

    window.removeEventListener("beforeunload", unloadHandler);
    window.addEventListener("beforeunload", unloadHandler)
}

export const removeLocal = (key: string) => {
    const store = ls.get(LOCAL_CACHE_KEY);
    if (store?.[key]) {
        Reflect.deleteProperty(store, key);
        ls.set(LOCAL_CACHE_KEY, store)
    }
}

export const clearLocal = () => {
    ls.set(LOCAL_CACHE_KEY, {})
}

export const setLocal = (key: string, value: any, expire = DEFAULT_CACHE_TIME) => {
    const store = ls.get(LOCAL_CACHE_KEY) || {};
    store[key] = {
        value, expire: expire > 0 ? Date.now() + expire * 1000 : -1
    }
    ls.set(LOCAL_CACHE_KEY, store)
}

export const getLocal = <T = any>(key: string): null | T => {
    try {
        const {value, expire} = ls.get(LOCAL_CACHE_KEY)?.[key] || {};
        if (isStorageValid({value, expire})) {
            return value as T
        } else {
            removeLocal(key)
        }
    } catch (e) {
        return null
    }
    return null
}

//+-----------------------------------------------------------
// sessionStorage
//+-----------------------------------------------------------
export const removeSession = (key: string) => {
    const store = ss.get(SESSION_CACHE_KEY);
    if (store?.[key]) {
        Reflect.deleteProperty(store, key);
        ss.set(SESSION_CACHE_KEY, store)
    }
}

export const clearSession = () => {
    ss.set(SESSION_CACHE_KEY, {})
}

export const setSession = (key: string, value: any, expire = DEFAULT_CACHE_TIME) => {
    const store = ss.get(SESSION_CACHE_KEY) || {};
    store[key] = {
        value, expire: expire > 0 ? Date.now() + expire * 1000 : -1
    }
    ss.set(SESSION_CACHE_KEY, store)
}

export const getSession = <T = any>(key: string): T | null => {
    try {
        const {value, expire} = ss.get(SESSION_CACHE_KEY)?.[key] || {};
        if (isStorageValid({value, expire})) {
            return value as T
        } else {
            removeSession(key)
        }
    } catch (e) {
        return null
    }
    return null
}

//+-----------------------------------------------------------
// memory
//+-----------------------------------------------------------

let mm: Record<string, Storage> = {}

export const removeMemory = (key: string) => delete mm[key]

export const clearMemory = () => mm = {}

export const setMemory = (key: string, value: any, expire = DEFAULT_CACHE_TIME) => {
    mm[key] = {
        value, expire: expire > 0 ? Date.now() + expire * 1000 : -1
    }
}

export const getMemory = <T>(key: string): T | null => {
    try {
        const {value, expire} = mm[key] || {};
        if (isStorageValid({value, expire})) {
            return value as T
        } else {
            removeMemory(key)
        }
    } catch (e) {
        return null
    }
    return null
}